﻿using System.Collections.Generic;

namespace Mozi.Live.SDP
{
    //Session description
    //  v=  (protocol version)
    //  o= (originator and session identifier)
    //  s=  (session name)
    //  i= *(session information)
    //  u=* (URI of description)
    //  e=* (email address)
    //  p= *(phone number)
    //  c=* (connection information -- not required if included in all media descriptions)
    //  b=* (zero or more bandwidth information lines)
    //  One or more time descriptions:
    //    ("t=", "r=" and "z=" lines; see below)
    //  k=* (obsolete)
    //  a= *(zero or more session attribute lines)
    //  Zero or more media descriptions
    //
    //Time description
    //  t= (time the session is active)
    //  r=* (zero or more repeat times)
    //  z=* (optional time zone offset line)

    //Media description, if present
    //   m= (media name and transport address)
    //   i=* (media title)
    //   c= *(connection information -- optional if included at session level)
    //   b=* (zero or more bandwidth information lines)
    //   k=* (obsolete)
    //   a= *(zero or more media attribute lines)

    //参考RFC8866
    /// <summary>
    /// SDP数据包，UTF-8字符串格式
    /// </summary>
    public class SDPPackage
    {
        /// <summary>
        /// 会话信息
        /// </summary>
        public SDPSessionDescription Session { get; set; }

        /// <summary>
        /// 媒体信息
        /// </summary>
        public List<SDPMediaDescription> Medias { get; set; }
        /// <summary>
        /// 转为SDP格式文本
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            List<string> sb = new List<string>();
            sb.Add(Session.ToString());
            if (Medias != null)
            {
                foreach (var md in Medias)
                {
                    sb.Add(md.ToString());
                }
            }
            return string.Join("\r\n", sb);
        }
        /// <summary>
        /// 解析
        /// </summary>
        /// <param name="doc"></param>
        /// <returns></returns>
        public static SDPPackage Parse(string doc)
        {
            SDPPackage pack = new SDPPackage
            {
                Session = new SDPSessionDescription(),
                Medias = new List<SDPMediaDescription>()
            };
            var session = pack.Session;
            session.BandWidths = new List<SDPBandWidth>();
            session.Attributes = new List<SDPAttribute>();
            session.TimeDescriptions = new List<SDPTimeDescription>();

            //session           
            string lsession = doc.Substring(0, doc.IndexOf("m="));
            //media
            List<string> arrMedias = new List<string>();
            int indM =0;

            while ((indM= doc.IndexOf("m=",indM+1))>0)
            {
                int indMLast = doc.IndexOf("m=",indM+1);
                if (indMLast == -1)
                {
                    indMLast = doc.Length - 1;
                }
                arrMedias.Add(doc.Substring(indM, indMLast-indM));
            }

            //session 
            string[] sessionItems = lsession.Split(new char[] { '\r', '\n' }, System.StringSplitOptions.RemoveEmptyEntries);

            foreach (var l in sessionItems)
            {
               
                string key = l.Substring(0,l.IndexOf('=')).Trim(), value=l.Substring(l.IndexOf('=')+1);
                switch (key)
                {
                    case "v":
                            session.Version = int.Parse(value);
                        break;
                    case "o":
                            session.Origin = SDPOrigin.Parse(value);
                            break;
                    case "s":
                            session.SessionName = value;
                            break;
                    case "i":
                            session.SessionInformation = value;
                            break;
                    case "u":
                            session.Uri = value;
                            break;
                    case "e":
                            session.Email = value;
                            break;
                    case "p":
                            session.Phone = value;
                            break;
                    case "c":
                            session.Connection = SDPConnection.Parse(value);
                            break;
                    case "b":
                            var bandwidth = SDPBandWidth.Parse(value);
                            session.BandWidths.Add(bandwidth);
                            break;
                    case "k":
                            session.EncryptKeys = value;
                            break;
                    case "a":
                            var attr = SDPAttribute.Parse(value);
                            session.Attributes.Add(attr);
                            break;
                    //以下三项一起解析
                    case "t":
                        SDPTimeActive ta = SDPTimeActive.Parse(value);
                        SDPTimeDescription sd = new SDPTimeDescription() { TimeRepeats = new List<SDPRepeatTime>() };
                        sd.TimeActive = ta;
                        break;
                    case "r":
                        if (session.TimeDescriptions.Count > 0)
                        {
                            var td = session.TimeDescriptions.FindLast(x => 1 == 1);
                            var tr = SDPRepeatTime.Parse(value);
                            td.TimeRepeats.Add(tr);
                        }

                        break;
                    case "z":
                        session.TimezoneAdjust = value;
                            break;
                }
            }
            //media 
            foreach(var lmedia in arrMedias) 
            {
                string[] lineMedia = lmedia.Split(new char[] { '\r', '\n' }, System.StringSplitOptions.RemoveEmptyEntries);
                SDPMediaDescription media = new SDPMediaDescription();
                media.BandWidths = new List<SDPBandWidth>();
                media.Attributes = new List<SDPAttribute>();
                foreach (var l in lineMedia)
                {
                    string key = l.Substring(0, l.IndexOf('=')).Trim(), value = l.Substring(l.IndexOf('=') + 1);
                    switch (key)
                    {
                        case "m":
                            media.Media = SDPMedia.Parse(value);
                            break;
                        case "i":
                            media.MediaTitle = value;
                            break;
                        case "c":
                            media.Connection = SDPConnection.Parse(value);
                            break;
                        case "b":
                            var bandwidth = SDPBandWidth.Parse(value);
                            media.BandWidths.Add(bandwidth);
                            break;
                        case "k":
                            media.EncryptKeys = value;
                            break;
                        case "a":
                            media.Attributes.Add(SDPAttribute.Parse(value));
                            break;
                    }
                }
                pack.Medias.Add(media);
            }
            return pack;
        }
        /// <summary>
        /// 增加媒体
        /// </summary>
        /// <param name="media"></param>
        /// <returns></returns>
        public SDPPackage AddMedia(SDPMediaDescription media)
        {
            if (Medias == null)
            {
                Medias = new List<SDPMediaDescription>();
            }
            Medias.Add(media);
            return this;
        }
        /// <summary>
        /// 按媒体格式类型查找媒体
        /// </summary>
        /// <param name="payloadType"></param>
        /// <returns></returns>
        public SDPMediaDescription FindMedia(int payloadType)
        {
            SDPMediaDescription media=null;
            if (Medias != null)
            {
                foreach (var r in Medias)
                {
                    SDPAttribute[] atts = r.GetAttributes(SDPAttributeType.RtpMap);
                    foreach(var a in atts)
                    {
                        SDPAttributeRTPMap smfp = SDPAttributeRTPMap.Parse((string)a.Value);
                        if (smfp.PayloadType == payloadType)
                        {
                            media = r;
                            break;
                        }
                    }
                }
            }
            return media;
        }
    }
}
